package com.jboltai.capability.vdb.types.milvus;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.jboltai.capability.embedding.JBoltEmbedding;
import com.jboltai.capability.model.JBoltAI;
import com.jboltai.capability.vdb.JBoltVDB;
import com.jboltai.capability.vdb.VDBOptResult;
import com.jboltai.capability.vdb.collection.VDBCollection;
import com.jboltai.capability.vdb.collection.field.VDBField;
import com.jboltai.capability.vdb.collection.field.VDBFieldType;
import com.jboltai.capability.vdb.doc.VDBDoc;
import com.jboltai.capability.vdb.search.VDBConditions;
import com.jboltai.capability.vdb.search.VDBFilter;
import com.jboltai.config.JBoltAIConfig;
import com.jboltai.event.embedding.EmbeddingEvent;
import com.jboltai.resource.embedding.EmbeddingModel;
import com.jboltai.resource.vdb.VDBResource;
import com.jboltai.util.other.StringUtil;
import com.jboltai.util.text.TextUtil;
import io.milvus.common.clientenum.FunctionType;
import io.milvus.grpc.*;
import io.milvus.param.R;
import io.milvus.param.dml.SearchParam;
import io.milvus.response.DescIndexResponseWrapper;
import io.milvus.response.QueryResultsWrapper;
import io.milvus.v2.client.MilvusClientV2;
import io.milvus.v2.common.ConsistencyLevel;
import io.milvus.v2.common.DataType;
import io.milvus.v2.common.IndexParam;
import io.milvus.v2.service.collection.request.*;
import io.milvus.v2.service.collection.response.DescribeCollectionResp;
import io.milvus.v2.service.database.request.CreateDatabaseReq;
import io.milvus.v2.service.database.request.DropDatabaseReq;
import io.milvus.v2.service.index.request.DescribeIndexReq;
import io.milvus.v2.service.index.response.DescribeIndexResp;
import io.milvus.v2.service.vector.request.*;
import io.milvus.v2.service.vector.request.data.EmbeddedText;
import io.milvus.v2.service.vector.request.data.FloatVec;
import io.milvus.v2.service.vector.request.ranker.BaseRanker;
import io.milvus.v2.service.vector.request.ranker.RRFRanker;
import io.milvus.v2.service.vector.response.InsertResp;
import io.milvus.v2.service.vector.response.QueryResp;
import io.milvus.v2.service.vector.response.SearchResp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * Milvus向量数据库
 */
public class JBoltMilvusVDB extends JBoltVDB {
    private static final Logger LOGGER = LoggerFactory.getLogger(JBoltAIConfig.LOGGER_NAME);
    /**
     * 数据库
     */
    private String database;


    private MilvusClientV2 client;

    /**
     * 集合数据加载到内存中的状态
     */
    private ConcurrentHashMap<String, Boolean> COLLECTION_LOAD_STATE = new ConcurrentHashMap<>();

    /**
     * 集合信息
     */
    private ConcurrentHashMap<String, CreateCollectionReq.CollectionSchema> COLLECTION_SCHEMA = new ConcurrentHashMap<>();
    /**
     * 向量索引信息
     */
    private ConcurrentHashMap<String, DescribeIndexResp.IndexDesc> COLLECTION_INDEX = new ConcurrentHashMap<>();


    public JBoltMilvusVDB(VDBResource resource) {
        super(resource);
        this.client = ((MilvusVDBResource) resource).getClient();
        if (StrUtil.isBlank(resource.getDatabase())) {
            throw new RuntimeException("向量数据库资源[" + resource.getId() + "]未设置database");
        }
        if (EmbeddingModel.isTencentInnerModel(resource.getEmbeddingModel())) {
            throw new RuntimeException(StrUtil.format("向量数据库资源[{}]不支持使用[{}]Embedding模型", resource.getId(), resource.getEmbeddingModel()));
        }
        this.database = resource.getDatabase();

    }

    @Override
    public VDBOptResult<Boolean> createCollection(VDBCollection collection) {
        if (resource.getEmbeddingModel() == null) {
            throw new RuntimeException("向量数据库[" + resource.getId() + "]未指定Embedding模型");
        }
        try {
            Integer shardNum = collection.getShardNum();
            if (shardNum == null) {
                shardNum = this.resource.getDefaultShardNum();
            }

            CreateCollectionReq.CreateCollectionReqBuilder<?, ?> builder = CreateCollectionReq.builder();
            builder.collectionName(collection.getName())
                    .numShards(shardNum);
            builder.indexParams(new ArrayList<>());
            if (collection.getDescription() != null) {
                builder.description(collection.getDescription());
            }
            CreateCollectionReq.CollectionSchema schema = client.createSchema();
            schema.setEnableDynamicField(true);
            builder.collectionSchema(schema);

            for (VDBField field : collection.getFields()) {
                processField(builder, schema, field);
            }
            client.createCollection(builder.build());
            return new VDBOptResult<>(true, true);
        } catch (Exception e) {
            return new VDBOptResult<>(e.getMessage(), e);
        }
    }


    private void processField(CreateCollectionReq.CreateCollectionReqBuilder<?, ?> collectionBuilder,
                              CreateCollectionReq.CollectionSchema fieldSchema, VDBField field) {
        boolean fullTextSearch = field.isFullTextSearch();
        AddFieldReq.AddFieldReqBuilder<?, ?> builder = AddFieldReq.builder();
        builder.fieldName(field.getName());
        DataType type;
        switch (field.getVdbFieldType()) {
            case STRING:
                type = DataType.VarChar;
                break;
            case NUMBER32:
                type = DataType.Int32;
                break;
            case NUMBER64:
                type = DataType.Int64;
                break;
            case VECTOR:
                type = DataType.FloatVector;
                break;
            default:
                throw new RuntimeException("不支持的字段类型:" + field.getVdbFieldType());
        }
        builder.dataType(type);
        if (field.isPrimaryKey()) {
            builder.isPrimaryKey(true);
            //主键字段
            if (field.getVdbFieldType() == VDBFieldType.STRING) {
                builder.maxLength(40);
            }
        } else if (field.getVdbFieldType() == VDBFieldType.VECTOR) {
            builder.dimension(resource.getEmbeddingModel().getDimension());
            processVectorFieldIndex(collectionBuilder, field);
        } else if (field.getVdbFieldType() == VDBFieldType.STRING) {
            //milvus强制 varchar类型指定最大长度
            builder.maxLength(65535);
            if (field.isFullTextSearch()) {
                builder.enableAnalyzer(true);
                Map<String, Object> analyzerParams = new HashMap<>();
                analyzerParams.put("type", "chinese");
                builder.analyzerParams(analyzerParams);
            }

        }
        fieldSchema.addField(builder.build());
        if (fullTextSearch) {
            //开启全文搜索的，需要创建一个额外的字段存储生成的稀疏向量
            AddFieldReq docSparse = AddFieldReq.builder()
                    .fieldName(field.getName() + "_sparse")
                    .dataType(DataType.SparseFloatVector)
                    .build();
            fieldSchema.addField(docSparse);

            //还要定义个function，负责将文本转换为稀疏向量表示
            fieldSchema.addFunction(CreateCollectionReq.Function.builder()
                    .functionType(FunctionType.BM25).name(field.getName() + "_bm25_emb")
                    .inputFieldNames(Collections.singletonList(field.getName()))
                    .outputFieldNames(Collections.singletonList(field.getName() + "_sparse"))
                    .build());

            //添加索引
            collectionBuilder.indexParam(IndexParam.builder()
                    .fieldName(field.getName() + "_sparse")
                    .indexType(IndexParam.IndexType.SPARSE_INVERTED_INDEX)
                    .metricType(IndexParam.MetricType.BM25)
                    .build());

        }
    }


    /**
     * 处理向量字段
     *
     * @param field
     */
    private void processVectorFieldIndex(CreateCollectionReq.CreateCollectionReqBuilder<?, ?> collectionBuilder, VDBField field) {
        IndexParam.IndexParamBuilder<?, ?> builder = IndexParam.builder();
        //向量字段
        IndexParam.MetricType metricType;
        switch (field.getVdbMetricType()) {
            case DEFAULT:
                metricType = IndexParam.MetricType.COSINE;
                break;
            case L2:
                metricType = IndexParam.MetricType.L2;
                break;
            case IP:
                metricType = IndexParam.MetricType.IP;
                break;
            default:
                throw new RuntimeException("不支持的向量索引类型:" + field.getVectorIndexType());
        }
        builder.metricType(metricType);
        JSONObject params = field.getVectorIndexParams();

        IndexParam.IndexType indexType;
        switch (field.getVectorIndexType()) {
            case FLAT:
                indexType = IndexParam.IndexType.FLAT;

                break;
            case HNSW: {
                indexType = IndexParam.IndexType.HNSW;
                break;
            }


            case IVF_FLAT: {
                if (params == null || params.containsKey("nlist") == false) {
                    throw new RuntimeException("IVF_FLAT类型的向量字段必须指定nlist参数");
                }
                indexType = IndexParam.IndexType.IVF_FLAT;
                break;
            }

            case IVF_PQ: {
                if (params == null) {
                    throw new RuntimeException("IVF_PQ类型的向量字段必须指定参数");
                }
                Integer nlist = params.getInteger("nlist");
                if (nlist == null) {
                    throw new RuntimeException("IVF_PQ类型的向量字段必须指定nlist参数");
                }
                Integer m = params.getInteger("M");
                if (m == null) {
                    throw new RuntimeException("IVF_PQ类型的向量字段必须指定M参数");
                }
                indexType = IndexParam.IndexType.IVF_PQ;
                break;
            }
            case IVF_SQ8: {
                if (params == null || params.containsKey("nlist") == false) {
                    throw new RuntimeException("IVF_SQ8类型的向量字段必须指定nlist参数");
                }
                indexType = IndexParam.IndexType.IVF_SQ8;
                break;
            }
            default:
                throw new RuntimeException("不支持的向量索引类型:" + field.getVectorIndexType());
        }
        builder.indexType(indexType);
        if (params != null) {
            builder.extraParams(params);
        }
        builder.fieldName(field.getName());
        builder.indexName(field.getName() + "_index");
        collectionBuilder.indexParam(builder.build());


    }

    @Override
    public VDBOptResult<Boolean> dropCollection(String name) {

        try {
            client.dropCollection(DropCollectionReq.builder().collectionName(name).build());
            COLLECTION_SCHEMA.remove(name);
            COLLECTION_LOAD_STATE.remove(name);
            COLLECTION_INDEX.remove(name);
            return new VDBOptResult<>(true, true);
        } catch (Exception e) {
            return new VDBOptResult<>(e.getMessage(), e);
        }


    }

    @Override
    public VDBOptResult<Boolean> checkCollectionExist(String name) {
        try {
            List<String> collectionNames = client.listCollections().getCollectionNames();
            return new VDBOptResult<>(true, collectionNames != null && collectionNames.contains(name));
        } catch (Exception e) {
            return new VDBOptResult<>(e.getMessage(), e);
        }
    }

    @Override
    public VDBOptResult<Boolean> truncateCollection(String name) {
        return this.delete(name, new VDBConditions().notEq("id", ""));
    }

    @Override
    public VDBOptResult<Integer> insert(String collectionName, VDBDoc... docs) {
        if (docs == null || docs.length == 0) {
            throw new IllegalArgumentException("docs不能为空");
        }
        try {
            List<JsonObject> list = new ArrayList<>();
            VDBOptResult<JsonObject> result = null;
            for (VDBDoc doc : docs) {

                result = generateDocument(doc);
                if (!result.isSuccess()) {
                    return new VDBOptResult<>(result.getMsg(), result.getException());
                }
                list.add(result.getResult());

            }
            InsertReq req = InsertReq.builder().collectionName(collectionName)
                    .data(list).build();
            InsertResp resp = client.insert(req);
            return new VDBOptResult<>(true, (int) resp.getInsertCnt());
        } catch (Exception e) {
            return new VDBOptResult<>(e.getMessage(), e);
        }
    }

    private VDBOptResult<JsonObject> generateDocument(VDBDoc doc) {
        if (doc.getDoc() == null) {
            throw new RuntimeException(StrUtil.format("向量文本字段doc内容不能为空：id[]", doc.getId()));
        }
        VDBOptResult<JsonObject> result = new VDBOptResult<>();

        JsonObject data = new JsonObject();
        if (doc.getId() != null) {
            data.addProperty(VDBField.ID_FIELD_NAME, doc.getId());
        }
        if (doc.getFieldValues() != null) {
            Iterator<String> iterator = doc.getFieldValues().keySet().iterator();
            while (iterator.hasNext()) {
                String key = iterator.next();
                Object v = doc.getFieldValues().get(key);
                if (v != null) {
                    if (v instanceof String) {
                        data.addProperty(key, (String) v);
                    } else if (v instanceof Boolean) {
                        data.addProperty(key, (Boolean) v);
                    } else {
                        data.addProperty(key, (Number) v);
                    }
                }
            }
        }
        LOGGER.info("开始VDB插入：{}" , doc.getDoc());
        data.addProperty(VDBField.DOC_FIELD_NAME, TextUtil.clean(doc.getDoc().toLowerCase()));
        EmbeddingEvent embedding = JBoltEmbedding.embedding(resource.getEmbeddingModel(), doc.getDoc());
        embedding.setAttrs("data", data);
        embedding.setAttrs("result", result);
        embedding.onSuccess((event, res) -> {
            JsonObject d = event.getAttr("data");
            VDBOptResult<JsonObject> r = event.getAttr("result");
            JsonArray vector = new JsonArray();
            res.get(0).stream().forEach(item -> vector.add(item.floatValue()));
            //Milvus vector需要转成float类型
            d.add(VDBField.VECTOR_FIELD_NAME, vector);
            r.setSuccess(true);
            r.setResult(d);

        }).onFail((event, err) -> {
            result.setSuccess(false);
            result.setMsg("Embedding发生异常：" + err.getFullMsg());
            result.setException(err.getException());
        }).publish().await();
        return result;
    }


    @Override
    public VDBOptResult<Integer> update(String collectionName, VDBDoc doc, VDBConditions conditions) {
        throw new RuntimeException("Milvus数据库不支持update操作，请删除数据后，重新insert");
    }

    @Override
    public VDBOptResult<List<VDBCollection>> listCollections() {
        try {

            List<String> collectionNames = client.listCollections().getCollectionNames();
            if (collectionNames == null || collectionNames.isEmpty()) {
                return new VDBOptResult<>(true, Collections.emptyList());
            }
            return new VDBOptResult<>(true, collectionNames.stream().map(item -> {
                return new VDBCollection(this).setName(item);
            }).collect(Collectors.toList()));
        } catch (Exception e) {
            return new VDBOptResult(e.getMessage(), e);
        }
    }

    @Override
    public VDBOptResult<Void> createDatabase(String database) {
        try {

            CreateDatabaseReq req = CreateDatabaseReq.builder()
                    .databaseName(database)
                    .build();
            client.createDatabase(req);
            return new VDBOptResult<>(true, null);
        } catch (Exception e) {
            return new VDBOptResult(e.getMessage(), e);
        }
    }

    @Override
    public VDBOptResult<Boolean> checkDatabaseExist(String database) {
        try {

            List<String> databaseNames = client.listDatabases().getDatabaseNames();
            return new VDBOptResult<>(true, databaseNames != null && databaseNames.contains(database));
        } catch (Exception e) {
            return new VDBOptResult(e.getMessage(), e);
        }
    }

    @Override
    public VDBOptResult<Boolean> dropDatabase(String database) {
        try {

            DropDatabaseReq req = DropDatabaseReq.builder()
                    .databaseName(database)
                    .build();
            client.dropDatabase(req);
            return new VDBOptResult<>(true, null);
        } catch (Exception e) {
            return new VDBOptResult(e.getMessage(), e);
        }
    }


    /**
     * 加载集合数据到内存中
     *
     * @param collectionName
     * @return
     */
    private VDBOptResult<Boolean> loadCollectionToMemory(String collectionName) {
        try {
            //先检测加载状态
            GetLoadStateReq req = GetLoadStateReq.builder()
                    .collectionName(collectionName)
                    .build();
            Boolean loaded = client.getLoadState(req);
            if (loaded) {
                COLLECTION_LOAD_STATE.put(collectionName, true);
                return new VDBOptResult<>(true, true);
            }

            LoadCollectionReq loadCollectionReq = LoadCollectionReq.builder()
                    .collectionName(collectionName)
                    .build();
            client.loadCollection(loadCollectionReq);
            return new VDBOptResult<>(true, true);
        } catch (Exception e) {
            return new VDBOptResult(e.getMessage(), e);
        }

    }

    /**
     * 从内存中释放集合数据
     *
     * @param collectionName
     * @return
     */
    private VDBOptResult<Boolean> releaseCollectionToMemory(String collectionName) {
        try {
            ReleaseCollectionReq releaseCollectionReq = ReleaseCollectionReq.builder()
                    .collectionName(collectionName)
                    .build();

            client.releaseCollection(releaseCollectionReq);
            return new VDBOptResult<>(true, true);
        } catch (Exception e) {
            return new VDBOptResult(e.getMessage(), e);
        }
    }


    @Override
    public VDBOptResult<List<VDBDoc>> search(String collectionName, VDBConditions conditions) {
        if (conditions == null) {
            conditions = new VDBConditions();
        }
        if (collectionName == null) {
            throw new RuntimeException("未指定collectionName");
        }

        //Milvus查询前需要将数据加载到内存中
        if (COLLECTION_LOAD_STATE.contains(collectionName) == false) {
            VDBOptResult<Boolean> loadResult = loadCollectionToMemory(collectionName);
            if (loadResult.isSuccess() == false) {
                return new VDBOptResult<>(loadResult.getMsg(), loadResult.getException());
            }
        }


        try {

            if (conditions.getMatchText() == null || conditions.getMatchText().isEmpty()) {
                //精确查询
                return query(collectionName, conditions);
            } else {
                LOGGER.info("开始VDB查询：{}" , conditions.getMatchText().get(0));
                VDBOptResult<List<VDBDoc>> vectorSearch = vectorSearch(collectionName, conditions, false);
                if (!vectorSearch.isSuccess()) {
                    return vectorSearch;
                }

                boolean isFullTextSearch = isFullTextSearch(collectionName);
                if (!isFullTextSearch) {
                    //该集合不支持全文搜索，则直接返回
                    return vectorSearch;
                }
                //否则还要做全文搜索
                VDBOptResult<List<VDBDoc>> fullSearch = vectorSearch(collectionName, conditions, true);
                if (!fullSearch.isSuccess()) {

                    return fullSearch;
                }

                //归一化全文检索的分数
                normalizeFullSearchScore(fullSearch.getResult());

                List<VDBDoc> list = JBoltAI.rerankByRRF(fullSearch.getResult(), vectorSearch.getResult());
                if (list.size() > conditions.getLimit()) {
                    list = list.subList(0, conditions.getLimit());
                }
                return new VDBOptResult<>(true, list);


            }
        } catch (Exception e) {
            return new VDBOptResult<>(e);
        }

    }

    /**
     * Milvus全文检索使用bm25算法，分数范围无上限，这里要统一分数范围为0-1，避免出现分数过大的情况。
     * 统一算法是，从结果中取最大值，如果最大值不满10，则按10算，然后用每一个的得分除最大值，得到新的score
     * @param result
     */
    private void normalizeFullSearchScore(List<VDBDoc> result) {
        if (result == null || result.isEmpty()) {
            return;
        }

        // Find the maximum score
        double maxScore = result.stream()
                .mapToDouble(VDBDoc::getScore)
                .max()
                .orElse(0.0);

        // If maxScore is less than 10, use 10 as the denominator
        double denominator = Math.max(maxScore, 10.0);

        // Normalize all scores
        result.forEach(doc -> doc.setScore(doc.getScore() / denominator));
    }


    /**
     * 向量查询
     *
     * @param collectionName
     * @param conditions
     * @return
     */
    private VDBOptResult<List<VDBDoc>> vectorSearch(String collectionName, VDBConditions conditions, boolean fullTextSearch) {


        if (conditions.getLimit() == null) {
            //向量查询，默认topK 10
            conditions.limit(10);
        }
        SearchReq.SearchReqBuilder<?, ?> builder = SearchReq.builder()
                .collectionName(collectionName);

        VDBOptResult<Boolean> buildResult = processSearchBuilder(collectionName, conditions, builder, fullTextSearch);


        if (buildResult.isSuccess() == false) {
            //向量化可能出错
            return new VDBOptResult<>(buildResult.getMsg(), buildResult.getException());
        }

        SearchResp searchResp = client.search(builder.build());
        List<SearchResp.SearchResult> results = searchResp.getSearchResults().get(0);
        if (results == null || results.isEmpty()) {
            return new VDBOptResult<>(true, Collections.EMPTY_LIST);
        }
        List<VDBDoc> list = new ArrayList<>(results.size());
        for (SearchResp.SearchResult record : results) {
            list.add(convertResultWrapperToDoc(record));
        }
        return new VDBOptResult<>(true, list);



    }

    private VDBDoc convertResultWrapperToDoc(SearchResp.SearchResult record) {
        VDBDoc doc = new VDBDoc();
        doc.setScore(record.getScore().doubleValue());
        Iterator<Map.Entry<String, Object>> iterator = record.getEntity().entrySet().iterator();
        Map.Entry<String, Object> entry = null;
        while (iterator.hasNext()) {
            entry = iterator.next();
            if (entry.getKey().equals(VDBField.ID_FIELD_NAME)) {
                doc.setId(entry.getValue().toString());
            } else if (entry.getKey().equals(VDBField.DOC_FIELD_NAME)) {
                doc.setDoc(entry.getValue().toString());
            } else if (entry.getKey().equals(VDBField.VECTOR_FIELD_NAME)) {
                doc.setVector(
                        ((List<Float>) (entry.getValue()))
                                .stream()
                                .map(v -> v.doubleValue())
                                .collect(Collectors.toList())
                );
            } else {
                doc.setField(entry.getKey(), entry.getValue());
            }
        }
        return doc;
    }

    private VDBDoc convertResultToDoc(QueryResp.QueryResult record) {
        VDBDoc doc = new VDBDoc();
        Iterator<Map.Entry<String, Object>> iterator = record.getEntity().entrySet().iterator();
        Map.Entry<String, Object> entry = null;

        while (iterator.hasNext()) {
            entry = iterator.next();
            if (entry.getKey().equals(VDBField.ID_FIELD_NAME)) {
                doc.setId(entry.getValue().toString());
            } else if (entry.getKey().equals(VDBField.DOC_FIELD_NAME)) {
                doc.setDoc(entry.getValue().toString());
            } else if (entry.getKey().equals(VDBField.VECTOR_FIELD_NAME)) {
                doc.setVector(
                        ((List<Float>) (entry.getValue()))
                                .stream()
                                .map(v -> v.doubleValue())
                                .collect(Collectors.toList())
                );
            } else if (entry.getKey().equals("distance")) {
                //Milvus将score字段命名为distance
                doc.setScore(StringUtil.convert(entry.getValue().toString(), Double.class));
            } else {
                doc.setField(entry.getKey(), entry.getValue());
            }
        }
        return doc;
    }


    /**
     * 精准查询
     *
     * @param conditions
     * @return
     */
    private VDBOptResult<List<VDBDoc>> query(String collectionName, VDBConditions conditions) {

        try {
            QueryReq.QueryReqBuilder<?, ?> builder = QueryReq.builder();
            builder.collectionName(collectionName);
            processQueryBuilder(collectionName, conditions, builder);

            QueryResp query = client.query(builder.build());
            List<QueryResp.QueryResult> records = query.getQueryResults();
            List<VDBDoc> list = new ArrayList<>(records.size());
            for (QueryResp.QueryResult record : records) {
                list.add(convertResultToDoc(record));
            }
            return new VDBOptResult<>(true, list);

        } catch (Exception e) {
            return new VDBOptResult<>(e.getMessage(), e);
        }

    }

    /**
     * 精准查询
     *
     * @param conditions
     * @return
     */
    private VDBOptResult<Boolean> processQueryBuilder(String collectionName, VDBConditions conditions, QueryReq.QueryReqBuilder builder) {
        if (conditions.getFilter() != null) {
            ;
            builder.filter(generateFilterExpr(conditions.getFilter()));
        }
        if (conditions.getSelect() != null) {
            //处理select字段
            List<String> selects = new ArrayList<>();
            for (String s : conditions.getSelect()) {
                String[] split = s.split(",");//用户可能传入["id,name"]
                for (String sub : split) {
                    sub = sub.trim();
                    if (StrUtil.isBlank(sub)) continue;

                    selects.add(sub);
                }
            }
            builder.outputFields(selects);
        } else {
            builder.outputFields(getFieldsWithoutIndex(collectionName));
        }

        if (conditions.getConsistencyLevel() != null) {
            builder.consistencyLevel(getConsistencyLevel(conditions.getConsistencyLevel()));
        }

        if (conditions.getLimit() != null) {
            builder.limit(conditions.getLimit());
        }
        if (conditions.getOffset() != null) {
            builder.offset(conditions.getOffset());
        }
        return new VDBOptResult<>(true, true);

    }

    private VDBOptResult<Boolean> processSearchBuilder(String collectionName, VDBConditions conditions,
                                                       SearchReq.SearchReqBuilder builder, boolean fullTextSearch) {
        if (conditions.getFilter() != null) {
            ;
            builder.filter(generateFilterExpr(conditions.getFilter()));
        }
        if (conditions.getSelect() != null) {
            //处理select字段
            List<String> selects = new ArrayList<>();
            for (String s : conditions.getSelect()) {
                String[] split = s.split(",");//用户可能传入["id,name"]
                for (String sub : split) {
                    sub = sub.trim();
                    if (StrUtil.isBlank(sub)) continue;

                    selects.add(sub);
                }
            }
            builder.outputFields(selects);
        } else {
            builder.outputFields(getFieldsWithoutIndex(collectionName));
        }

        if (conditions.getConsistencyLevel() != null) {
            builder.consistencyLevel(getConsistencyLevel(conditions.getConsistencyLevel()));
        }
        if (fullTextSearch) {

            builder.annsField(VDBField.DOC_FIELD_NAME + "_sparse");
            builder.data(conditions.getMatchText().stream().map(item -> new EmbeddedText(TextUtil.clean(item.toLowerCase()))).collect(Collectors.toList()));
        } else {
            builder.annsField(VDBField.VECTOR_FIELD_NAME);
            EmbeddingEvent event = JBoltEmbedding.embedding(resource.getEmbeddingModel(), conditions.getMatchText());
            event.onSuccess((e, res) -> {
                //milvus需要转成List<Float>
                builder.data(
                        res.stream().map(
                                item -> new FloatVec(item.stream().map(v -> v.floatValue()).collect(Collectors.toList()))
                        ).collect(Collectors.toList()));
            }).publish().await();
            if (event.getError() != null) {
                return new VDBOptResult<>(event.getError().getFullMsg(), event.getError().getException());
            }
            processVectorParams(collectionName, conditions, builder);

        }

        if (conditions.getLimit() != null) {
            builder.topK(conditions.getLimit());
        }
        if (conditions.getOffset() != null) {
            builder.offset(conditions.getOffset());
        }

        return new VDBOptResult<>(true, true);

    }


    private ConsistencyLevel getConsistencyLevel(com.jboltai.capability.vdb.search.ConsistencyLevel consistencyLevel) {
        switch (consistencyLevel) {
            case STRONG:
                return ConsistencyLevel.STRONG;
            case EVENTUAL:
                return ConsistencyLevel.EVENTUALLY;
            case DEFAULT:
                return ConsistencyLevel.BOUNDED;
            default:
                throw new RuntimeException("未适配的ConsistencyLevel");

        }
    }


    private void processVectorParams(String collectionName, VDBConditions conditions, SearchReq.SearchReqBuilder builder) {
        //向量化查询，可能需要传递一些参数
        DescribeIndexResp.IndexDesc indexDesc = getIndexDesc(collectionName);
        Map<String, Object> searchParams = new HashMap<>();
        switch (indexDesc.getIndexType()) {
            case HNSW:
                Integer ef = conditions.getVectorParam("ef", Integer.class);
                searchParams.put("ef", ef == null ? conditions.getLimit() : ef);
                break;
            case IVF_FLAT:
            case IVF_PQ:
            case IVF_SQ8:
                Integer nprobe = conditions.getVectorParam("nprobe", Integer.class);
                searchParams.put("nprobe", nprobe);
                break;
        }
        builder.metricType(indexDesc.getMetricType());
        builder.searchParams(searchParams);
    }

    /**
     * 精准查询
     *
     * @param filter
     * @return
     */
    private String generateFilterExpr(List<VDBFilter> filter) {
        String link = "";
        StringBuilder cond = new StringBuilder();
        for (VDBFilter item : filter) {
            if (link != null) {
                cond.append(link);
            } else {
                cond.append(" and ");
            }
            switch (item.getOpt()) {
                case AND:
                    link = " and ";
                    continue;
                case OR:
                    link = " or ";
                    continue;
                case AND_NOT:
                    link = " and not ";
                    continue;
                case OR_NOT:
                    link = " or not ";
                    continue;
                case BRACKET_LEFT:
                    link = " (";
                    continue;
                case BRACKET_RIGHT:
                    link = " )";
                    continue;
                case EQ:
                    cond.append(item.getName()).append("==").append(parseValue(item));
                    break;
                case NOT_EQ:
                    cond.append(item.getName()).append("!=").append(parseValue(item));
                    break;
                case GT:
                    cond.append(item.getName()).append(">").append(parseValue(item));
                    break;
                case GE:
                    cond.append(item.getName()).append(">=").append(parseValue(item));
                    break;
                case LT:
                    cond.append(item.getName()).append("<").append(parseValue(item));
                    break;
                case LE:
                    cond.append(item.getName()).append("<=").append(parseValue(item));
                    break;
                case IN:
                    cond.append(item.getName()).append(" in [").append(parseValue(item)).append("]");
                    break;
                case NOT_IN:
                    cond.append(item.getName()).append(" not in [").append(parseValue(item)).append("]");
                    break;
                default:
                    throw new RuntimeException("不支持的过滤条件:" + item.getOpt());
            }

            link = null;
        }
        return cond.toString();
    }

    private String parseValue(VDBFilter filter) {
        if (filter.getValue() == null) {
            throw new RuntimeException(filter.getName() + "的值不能为null");
        }
        Object value = filter.getValue();
        if (value instanceof String) {
            return "\"" + value + "\"";
        } else if (value instanceof String[]) {
            return Arrays.stream(((String[]) value)).map(x -> "\"" + x + "\"").collect(Collectors.joining(","));
        } else {
            return value.toString();
        }
    }

    /**
     * 获取集合的描述信息
     *
     * @param collectionName
     * @return
     */
    private CreateCollectionReq.CollectionSchema getColletionMetadata(String collectionName) {
        CreateCollectionReq.CollectionSchema schema = COLLECTION_SCHEMA.get(collectionName);
        if (schema == null) {
            DescribeCollectionResp resp = client.describeCollection(DescribeCollectionReq.builder().collectionName(collectionName).build());
            schema = resp.getCollectionSchema();
            COLLECTION_SCHEMA.putIfAbsent(collectionName, schema);
        }
        return schema;

    }

    /**
     * 获取集合索引
     *
     * @param collectionName
     * @return
     */
    private DescribeIndexResp.IndexDesc getIndexDesc(String collectionName) {
        DescribeIndexResp.IndexDesc indexDesc = COLLECTION_INDEX.get(collectionName);
        if (indexDesc == null) {

            DescribeIndexReq.DescribeIndexReqBuilder<?, ?> builder = DescribeIndexReq.builder().collectionName(collectionName);
            builder.indexName(VDBField.VECTOR_FIELD_NAME + "_index");
            DescribeIndexResp describeIndexResp = client.describeIndex(builder.build());
            indexDesc = describeIndexResp.getIndexDescByFieldName(VDBField.VECTOR_FIELD_NAME);
        }

        return indexDesc;
    }


    private List<String> getFieldsWithoutIndex(String collectionName) {
        CreateCollectionReq.CollectionSchema metadata = getColletionMetadata(collectionName);
        List<String> fields = new ArrayList<>();
        for (CreateCollectionReq.FieldSchema field : metadata.getFieldSchemaList()) {
            if (Objects.equals(VDBField.VECTOR_FIELD_NAME, field.getName())) {
                continue;
            }
            if (field.getName().endsWith("_sparse")) {
                //全文索引稀疏向量字段
                continue;
            }
            fields.add(field.getName());
        }
        return fields;
    }

    /**
     * 该集合是否有全文索引
     *
     * @param collectionName
     * @return
     */
    private boolean isFullTextSearch(String collectionName) {
        CreateCollectionReq.CollectionSchema metadata = getColletionMetadata(collectionName);
        return metadata.getFieldSchemaList().stream()
                .anyMatch(item -> Objects.equals(item.getName(), VDBField.DOC_FIELD_NAME + "_sparse"));
    }


    @Override
    public VDBOptResult<Boolean> delete(String collectionName, VDBConditions conditions) {
        if (collectionName == null) {
            throw new RuntimeException("未指定collectionName");
        }
        if (conditions == null) {
            throw new RuntimeException("Milvus执行删除必须指定filter条件");
        }
        List<VDBFilter> filter = conditions.getFilter();
        if (filter == null || filter.isEmpty()) {
            throw new RuntimeException("Milvus执行删除必须指定filter条件");
        }

        try {
            if (!COLLECTION_LOAD_STATE.contains(collectionName)) {
                VDBOptResult<Boolean> loadResult = loadCollectionToMemory(collectionName);
                if (!loadResult.isSuccess()) {
                    return new VDBOptResult<>(loadResult.getMsg(), loadResult.getException());
                }
            }

            DeleteReq.DeleteReqBuilder<?, ?> builder = DeleteReq.builder();
            builder.collectionName(collectionName);
            builder.filter(generateFilterExpr(filter));
            client.delete(builder.build());
            return new VDBOptResult<>(true, true);
        } catch (Exception e) {
            return new VDBOptResult<>(e.getMessage(), e);
        }

    }

    public MilvusClientV2 getClient() {
        return client;
    }


}
